// Copyright (c) Microsoft Corporation.  All rights reserved.
#include "Common.h"


CContentViewBase::~CContentViewBase()
{
    _ClearViewItems();
}

HRESULT CContentViewBase::_ClearViewItems()
{
    for (size_t i = 0; i < m_items.size(); i++)
    {
        delete m_items[i];
    }
    m_items.clear();
    
    return S_OK;
}

HRESULT CContentViewBase::AddViewItem(CViewItem* pItem)
{
    HRESULT hr = S_OK;
    
    try
    {
        m_items.push_back(pItem);
    }
    catch (...)
    {
        hr = E_FAIL;
    }
    
    return hr;
}

//
// common base OnSize routine for all views.
//
HRESULT CContentViewBase::OnSize(HDC hDC, LONG nClientWidth, LONG nClientHeight)
{
    HRESULT hr = S_OK;
    
    m_nClientWidth  = nClientWidth;
    m_nClientHeight = nClientHeight;
    
    m_nContentHeight = 0;

    SetFlag(CONTENT_VIEW_FLAG_LAYOUT_VALID);

    ClearFlag(CONTENT_VIEW_FLAG_SCROLLBAR);

    for(size_t i = 0; i < m_items.size(); i++)
    {
        CViewItem* pViewItem = m_items[i];

        hr = pViewItem->CalcItemHeight(hDC, m_nClientWidth, m_nClientHeight);

        if (SUCCEEDED(hr))
        {
            pViewItem->SetYOffset(m_nContentHeight);

            m_nContentHeight += pViewItem->GetHeight();

            if (!IsFlagSet(CONTENT_VIEW_FLAG_NOSCROLLBAR) &&
                !IsFlagSet(CONTENT_VIEW_FLAG_SCROLLBAR) &&
                m_nContentHeight > m_nClientHeight)
            {
                //
                // Scrollbar is needed.
                //
                SetFlag(CONTENT_VIEW_FLAG_SCROLLBAR);

                //
                // Shrink client width and recalculate content height
                //
#if SCROLLBAR_WIDTH != 0
                m_nClientWidth -= SCROLLBAR_WIDTH;
                m_nContentHeight = 0;

                for(size_t j = 0; j <= i; j++)
                {
                    hr = m_items[j]->CalcItemHeight(hDC, m_nClientWidth, m_nClientHeight);

                    if (SUCCEEDED(hr))
                    {
                        m_nContentHeight += m_items[j]->GetHeight();
                    }
                    else
                    {
                        break;
                    }
                }
#endif
            }
        }
        else
        {
            break;
        }
    }

    return hr;
}

HRESULT CContentViewBase::OnDeviceEvent(CDevice::DeviceEvent deviceEvent)
{
    UNREFERENCED_PARAMETER(deviceEvent);
    return S_FALSE;
}

HRESULT CContentViewBase::OnUpdateData(CScfContentBase* pScfContent)
{
    _ClearViewItems();

    ClearFlag(CONTENT_VIEW_FLAG_LAYOUT_VALID);  // invalidate content layout

    return Initialize(pScfContent);
}

SIZE CContentViewBase::DrawChild(HDC hdc,  int left, int top, int right, int bottom, int iChild, CViewItem* pChild)
{
    UNREFERENCED_PARAMETER(iChild);
    UNREFERENCED_PARAMETER(pChild);

    SIZE size;
    
    size.cx = right - left;
    size.cy = bottom - top;

    pChild->Draw(hdc, left, top, right, bottom);

    return size;
}

VOID CContentViewBase::DrawScrollbar(HDC hdc,  int left, int top, int right, int bottom)
{
    // let renderer draw scrollbar
    RECT rect = { left, top, right, bottom };

    g_pDataManager->GetRenderer()->DrawScrollbar(
                                        hdc,
                                        rect,
                                        m_nViewTopOffset,
                                        m_nContentHeight);
}

//
// draw title bar icon and text
//
HRESULT CContentViewBase::OnNCPaint(HDC hdc, int left, int top, int right, int bottom)
{
    CNodeApplication *pAppNode = g_pDataManager->GetCurrentApplication();

    left += CONTENT_LEFT_MARGIN;
    
    SIZE size = pAppNode->GetMiniIcon()->Draw(hdc, left, ((bottom - top) - MINI_ICON_SIZE) / 2);

    if (size.cx)
    {
        left += size.cx + 6;
    }

    RECT rect = { left, top, right, bottom };

    DrawText(hdc, m_pScfContentBase->GetTitle(), -1, &rect, DT_LEFT | DT_VCENTER | DT_NOPREFIX | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS | DT_SINGLELINE);

    return S_OK;
}

//
// common paint routine for <content>, <menu> and <dialog>
//
HRESULT CContentViewBase::OnPaint(HDC hdc, int left, int top, int right, int bottom)
{
    HRESULT hr = S_OK;
    int yScreenOffset = top - m_nViewTopOffset;
    int yContentOffset = 0;
    HRGN hRgn;

    //
    // draw content
    //
    
    if (IsFlagSet(CONTENT_VIEW_FLAG_SCROLLBAR))
    {
        right -= SCROLLBAR_WIDTH;
    }
    
    hRgn = CreateRectRgn(left, top, right, bottom);
    
    if (hRgn)
    {
        SelectClipRgn(hdc, hRgn);
    }
    
#if DBG
    WCHAR buff[256];
    StringCchPrintf(buff, COUNTOF(buff), L"OnPaint ViewTopOffset=%d\r\n", m_nViewTopOffset);
    OutputDebugString(buff);
#endif

    for(size_t i = 0; i < m_items.size(); i++)
    {
        CViewItem *pViewItem = m_items[i];
        SIZE size = { 0 };

#if DBG
        StringCchPrintf(buff, COUNTOF(buff), L"[%d] yScrnOfst=%d yContOfst=%d", i, yScreenOffset, yContentOffset);
        OutputDebugString(buff);
#endif
        
        if (yContentOffset + pViewItem->GetHeight() > m_nViewTopOffset)
        {
            size = DrawChild(
                    hdc,
                    left,
                    yScreenOffset,
                    right,
                    yScreenOffset + pViewItem->GetHeight(),
                    (DWORD)i,
                    pViewItem);
#if DBG
            StringCchPrintf(buff, COUNTOF(buff), L" -> draw cy =%d", size.cy);
            OutputDebugString(buff);
#endif
        }
        else
        {
            size.cy = pViewItem->GetHeight();
        }

#if DBG
        OutputDebugString(L"\r\n");
#endif

        yContentOffset += size.cy;
        yScreenOffset  += size.cy;

        if (yScreenOffset > bottom)
        {
            break;
        }
    }

    if (hRgn)
    {
        SelectClipRgn(hdc, NULL);
        DeleteObject(hRgn);
    }

    //
    // draw scrollbar
    //
    if (IsFlagSet(CONTENT_VIEW_FLAG_SCROLLBAR))
    {
        DrawScrollbar(hdc, right, top, right + SCROLLBAR_WIDTH, bottom);
    }

    return hr;
}

//
// initialize <content> view
//
HRESULT CContentView::Initialize(CScfContentBase *pScfContentBase)
{
    HRESULT hr = S_OK;

    m_pScfContentBase = pScfContentBase;

    for(size_t i = 0; i < m_pScfContentBase->GetChildCount(); i++)
    {
        CScfElement *pElement = m_pScfContentBase->GetChild(i);
        CViewItem *pViewItem = NULL;
        
        if (pElement->GetElementType() == SCF_TXT_ELEMENT)
        {
            pViewItem = (CViewItem *)new(std::nothrow) CViewTextItem(pElement, this);
        }
        else if (pElement->GetElementType() == SCF_IMG_ELEMENT)
        {
            pViewItem = (CViewItem *)new(std::nothrow) CViewImageItem(pElement, this);
        }
        else if (pElement->GetElementType() == SCF_BR_ELEMENT)
        {
            pViewItem = (CViewItem *)new(std::nothrow) CViewBrItem(pElement, this);
        }
        else
        {
            hr = E_INVALIDARG;
            break;
        }

        if (SUCCEEDED(hr))
        {
            if (!pViewItem)
            {
                hr = E_OUTOFMEMORY;
                break;
            }
        }

        if (SUCCEEDED(hr))
        {
            hr = pViewItem->Initialize();

            if (SUCCEEDED(hr))
            {
                hr = AddViewItem(pViewItem);
            }

            if (FAILED(hr))
            {
                delete pViewItem;
                break;
            }
        }
    }
    
    return hr;
}

//
// calculate content height based on the given clientwidth.
//
HRESULT CContentView::OnSize(HDC hDC, LONG nClientWidth, LONG nClientHeight)
{
    HRESULT hr = S_OK;

    __super::OnSize(hDC, nClientWidth, nClientHeight);

    //
    // Adjusting ViewTopOffset
    //
    if (m_nViewTopOffset != 0)
    {
        if (m_nContentHeight <= m_nClientHeight)
        {
            m_nViewTopOffset = 0;
        }
        else if ((m_nViewTopOffset + m_nClientHeight) > m_nContentHeight)
        {
            m_nViewTopOffset = m_nContentHeight - m_nClientHeight;
        }
    }

    return hr;
}

//
// paint routine for <content>
// draw background first and call common paint routine.
//
HRESULT CContentView::OnPaint(HDC hdc, int left, int top, int right, int bottom)
{
    CScfContentElement *pContentElement = (CScfContentElement *)m_pScfContentBase;

    //
    // draw back ground.
    //
    if (pContentElement->m_bg != 0)
    {
        if (IsFlagSet(CONTENT_VIEW_FLAG_SCROLLBAR))
        {
            // shrink content width
            right -= SCROLLBAR_WIDTH;
        }
        
        CNodeApplication *pAppNode = g_pDataManager->GetCurrentApplication();

        CContent *pBgContent = pAppNode->GetContent(pContentElement->m_bg);
            
        if (pBgContent)
        {
            int nImageHeight = pBgContent->GetImageHeight();
            int nImageWidth = pBgContent->GetImageWidth();
            
            switch(pContentElement->m_bgfit)
            {
                case SCF_BGFIT_SCALE:
                {
                    pBgContent->DrawImage(
                                    hdc,
                                    left,           // x
                                    top,            // y
                                    right - left,   // cxDest
                                    bottom - top);  // cyDest
                    break;
                }
                case SCF_BGFIT_TILE:
                {
                    for(int y = top; y < bottom; y += nImageHeight)
                    {
                        for(int x = left; x < right; x += nImageWidth)
                        {
                            pBgContent->DrawImage(hdc, x, y, nImageWidth, nImageHeight);
                        }
                    }
                    break;
                }
                case SCF_BGFIT_CENTER:
                {
                    HRGN hRgn = CreateRectRgn(left, top, right, bottom);
                    
                    if (hRgn)
                    {
                        SelectClipRgn(hdc, hRgn);
                    }
                    
                    pBgContent->DrawImage(hdc,
                                    left + ((right - left) - nImageWidth) / 2,
                                    top + ((bottom - top) - nImageHeight) / 2,
                                    nImageWidth,
                                    nImageHeight);
                    
                    if (hRgn)
                    {
                        SelectClipRgn(hdc, NULL);
                        DeleteObject(hRgn);
                    }
                    break;
                }
            }
        }
        else
        {
            //
            // content missing
            //
            g_pDataManager->GetDDI()->SendContentMissingEvent(pAppNode->m_guidApplicationId, pContentElement->m_bg);
        }

        
        if (IsFlagSet(CONTENT_VIEW_FLAG_SCROLLBAR))
        {
            right += SCROLLBAR_WIDTH;
        }
    }

    return __super::OnPaint(hdc, left, top, right, bottom);
}
//
// Key event handler for <content>
//
// S_OK   : handled
// S_FALSE: not handled
// E_XXX  : error
//
HRESULT CContentView::OnDeviceEvent(CDevice::DeviceEvent DeviceEvent)
{
    HRESULT hr = S_FALSE;
    
    switch(DeviceEvent)
    {
        case CDevice::ButtonMenu:
        {
            CScfContentElement *pContentElement = (CScfContentElement *)m_pScfContentBase;
            
            //
            // menuId could be 0
            // default context menu will be used when menuId attribute is not specified.
            //
            g_pDataManager->SetContextMenu(pContentElement->m_menuId);
            
            hr = S_OK;
            break;
        }
        case CDevice::ButtonUp:
        {
            if (m_nViewTopOffset)
            {
                m_nViewTopOffset -= CRenderer::GetScrollAmountInPixels();

                if (m_nViewTopOffset < 0)
                {
                    m_nViewTopOffset = 0;
                }
                
                hr = S_OK;
            }
            break;
        }
        case CDevice::ButtonDown:
        {
            if (m_nViewTopOffset + m_nClientHeight < m_nContentHeight)
            {
                m_nViewTopOffset += CRenderer::GetScrollAmountInPixels();
                
                if (m_nViewTopOffset + m_nClientHeight > m_nContentHeight)
                {
                    m_nViewTopOffset = m_nContentHeight - m_nClientHeight;
                }
                
                hr = S_OK;
            }
            break;
        }
    }

    return hr;
};

//
// <dialog> initialize
//
HRESULT CDialogView::Initialize(CScfContentBase *pScfContentBase)
{
    CScfDialogElement *pDialogElement = (CScfDialogElement *)pScfContentBase;

    // special case for <dialog> imgid attribute
    if (pDialogElement->m_imgId != 0)
    {
        // create fake view item object for imgid attribute
        m_FakeImgElement.m_id = pDialogElement->m_imgId;
        
        CViewImageItem *pImgViewItem = new(std::nothrow) CViewImageItem(&m_FakeImgElement, this);
        
        if (pImgViewItem)
        {
            HRESULT hr = pImgViewItem->Initialize();
            
            if (SUCCEEDED(hr))
            {
                hr = AddViewItem(pImgViewItem);
            }

            if (FAILED(hr))
            {
                delete pImgViewItem;
            }
        }
    }
    
    // call <content> initialize
    return __super::Initialize(pScfContentBase);
}

//
// <dialog> size
//
HRESULT CDialogView::OnSize(HDC hDC, LONG nClientWidth, LONG nClientHeight)
{
    HRESULT hr = S_OK;

    m_nButtonWidth = 0;

    //
    // call base class OnSize
    //
    hr = __super::OnSize(hDC, nClientWidth, nClientHeight - DIALOG_BUTTON_AREA_HEIGHT);

    if (SUCCEEDED(hr) && m_pScfContentBase->GetButtonCount() != 0)
    {
        //
        // calculate button width
        //
        int nMaxBtnTextExtent = 0;
        int nExtentPerBtn = nClientWidth / (int)m_pScfContentBase->GetButtonCount();

        for(size_t i = 0; i < m_pScfContentBase->GetButtonCount(); i++)
        {
            CScfBtnElement *pBtnElement = m_pScfContentBase->GetButton(i);
            SIZE size = { 0, 0 };

            GetTextExtentPoint32(hDC, pBtnElement->GetText(), lstrlen(pBtnElement->GetText()), &size);

            if (size.cx > nMaxBtnTextExtent)
            {
                nMaxBtnTextExtent = size.cx;
            }
        }

        m_nButtonWidth = min(max(DIALOG_BUTTON_MIN_WIDTH, CONTENT_LEFT_MARGIN + nMaxBtnTextExtent + CONTENT_RIGHT_MARGIN), nExtentPerBtn);
    }

    return hr;
}

//
// <dialog> paint
//
HRESULT CDialogView::OnPaint(HDC hDC, int left, int top, int right, int bottom)
{
    //
    // draw content
    // Note: Calling CContentViewBase::OnPaint instead of __super::OnPaint to skip CContentView::OnPaint.
    //
    CContentViewBase::OnPaint(hDC, left, top, right, bottom - DIALOG_BUTTON_AREA_HEIGHT);

    //
    // buttons BG
    //
    RECT rect = { left, bottom - DIALOG_BUTTON_AREA_HEIGHT, right, bottom };

    g_pDataManager->GetRenderer()->DrawStatusbar(hDC, rect);

    //
    // draw buttons
    //
    if (m_pScfContentBase->GetButtonCount() != 0 && m_nButtonWidth != 0)
    {
        int xBtn = 0;
        int yBtn = bottom - DIALOG_BUTTON_AREA_HEIGHT + 1 + (DIALOG_BUTTON_AREA_HEIGHT - DIALOG_BUTTON_HEIGHT - 1) / 2;
        int nExtentPerBtn = m_nClientWidth / (int)m_pScfContentBase->GetButtonCount();

        for(size_t i = 0; i < m_pScfContentBase->GetButtonCount(); i++)
        {
            CScfBtnElement *pBtnElement = m_pScfContentBase->GetButton(i);

            rect.left = xBtn + (nExtentPerBtn - m_nButtonWidth) / 2;
            rect.top = yBtn;
            rect.right = rect.left + m_nButtonWidth;
            rect.bottom = yBtn + DIALOG_BUTTON_HEIGHT;

            g_pDataManager->GetRenderer()->DrawButton(
                    hDC,
                    rect,
                    pBtnElement->GetText(),
                    (DWORD)i == m_nCurrentBtn);     // bSelected

            xBtn += nExtentPerBtn;
        }
    }

    return S_OK;
}

//
// <dialog> key event handler
//
// S_OK   : handled
// S_FALSE: not handled
// E_XXX  : error
//
HRESULT CDialogView::OnDeviceEvent(CDevice::DeviceEvent DeviceEvent)
{
    HRESULT hr = S_FALSE;
    
    switch (DeviceEvent)
    {
        case CDevice::ButtonMenu:
        {
            // dialog doesn't have context menu
            hr = S_OK;
            break;
        }
        case CDevice::ButtonPrevious:
        {
            if (m_nCurrentBtn > 0)
            {
                m_nCurrentBtn--;
                hr = S_OK;
            }
            break;
        }
        case CDevice::ButtonNext:
        {
            if (m_nCurrentBtn < (m_pScfContentBase->GetButtonCount() - 1))
            {
                m_nCurrentBtn++;
                hr = S_OK;
            }
            break;
        }
        case CDevice::ButtonSelect:
        {
            CNodeApplication* pAppNode = g_pDataManager->GetCurrentApplication();
            CScfBtnElement* pBtn = m_pScfContentBase->GetButton(m_nCurrentBtn);

            g_pDataManager->GetDDI()->SendNavigationEvent(
                pAppNode->m_guidApplicationId,              // GadgetId
                g_pDataManager->GetCurrentContentId(),      // PreviousPage
                pBtn->m_targetId,           // TargetPage
                pBtn->m_btnKey
                );
            
            g_pDataManager->SetCurrentContentId(pBtn->m_targetId);
            
            CContent* pContent = pAppNode->GetContent(pBtn->m_targetId);
            if (pContent)
            {
                pContent->OnContentEnter();
            }
            
            hr = S_OK;
            break;
        }
    }
    
    if (hr == S_FALSE)
    {
        hr = __super::OnDeviceEvent(DeviceEvent);
    }
    
    return hr;
}

HRESULT CMenuView::Initialize(CScfContentBase *pScfContentBase)
{
    HRESULT hr = S_OK;
    CScfElement *pPrevElement = NULL;
    BOOL bFirstDefItem = TRUE;

    m_pScfContentBase = pScfContentBase;
    m_nDefItem = 0;
    
    for(size_t i = 0; i < m_pScfContentBase->GetChildCount(); i++)
    {
        CScfElement *pElement = m_pScfContentBase->GetChild(i);
        
        if (pElement->GetElementType() == SCF_DIV_ELEMENT)
        {
            // ignore contiguous <div> element
            if (pPrevElement && pPrevElement->GetElementType() == SCF_DIV_ELEMENT)
            {
                continue;
            }
            
            // ignore last <div> element
//          if (i == (m_pScfContentBase->GetChildCount() - 1))
//          {
//              break;
//          }
        }
        
        CViewMenuItem *pViewMenuItem = new(std::nothrow) CViewMenuItem(pElement, this);
        
        if (pViewMenuItem)
        {
            hr = pViewMenuItem->Initialize();
            
            if (SUCCEEDED(hr))
            {
                hr = AddViewItem(pViewMenuItem);
            }
            
            if (FAILED(hr))
            {
                delete pViewMenuItem;
                break;
            }
        }
        else
        {
            hr = E_OUTOFMEMORY;
            break;
        }

        // set def item
        if (bFirstDefItem)
        {
            if (pElement->GetElementType() == SCF_ITEM_ELEMENT)
            {
                CScfItemElement *pItem = (CScfItemElement *)pElement;				

                if (pItem->m_def)
                {
                    m_nDefItem = m_items.size() - 1;
                    bFirstDefItem = FALSE;
                }
            }
        }
		
        pPrevElement = pElement;
    }
	
    //
    // update current item as def item, only when def item is specified.
    // this shouldn't be done when def item is not specified.
    // because menu content could be updated to change only title (e.g., media player gadget's volume content).
    // and in such case, keep the view state.
    //
    if (!bFirstDefItem)
    {
        CMenuView::OnEnter();
    }
    else
    {
        if (GetViewType() != SCF_CONTEXT_MENU_VIEW)
        {
            // Check for the case that item count is changed (e.g., add content n item -> view created -> add content n-1 item).
            if (m_nCurrentItem >= m_items.size())
            {
                m_nCurrentItem = m_items.size() - 1;

                _AdjustViewTopOffset();
            }
        }
    }
    
    return hr;
}

VOID CMenuView::OnEnter()
{
    m_nViewTopOffset = 0;

    m_nCurrentItem = m_nDefItem;

    if (m_nCurrentItem != 0)
    {
        _AdjustViewTopOffset();
    }
}

HRESULT CMenuView::OnSize(HDC hDC, LONG nClientWidth, LONG nClientHeight)
{
    HRESULT hr = S_OK;

    hr = __super::OnSize(hDC, nClientWidth, nClientHeight);

#if DBG
    WCHAR buff[256];
    StringCchPrintf(buff, COUNTOF(buff), L"CMenuView::OnSize\r\n");
    OutputDebugString(buff);

    for(size_t i = 0; i < m_items.size(); i++)
    {
        CViewMenuItem *pMenuViewItem = (CViewMenuItem  *)m_items[i];

        if (pMenuViewItem->IsLongSingleLineItem())
        {
            StringCchPrintf(buff, COUNTOF(buff), L"[%d] Height=%d SelHeight=%d\r\n", i, pMenuViewItem->GetHeight(), pMenuViewItem->GetSelectedItemHeight());
        }
        else
        {
            StringCchPrintf(buff, COUNTOF(buff), L"[%d] Height=%d\r\n", i, pMenuViewItem->GetHeight());
        }

        OutputDebugString(buff);
    }
#endif

    return hr;
}

SIZE CMenuView::DrawChild(HDC hdc, int left, int top, int right, int bottom, int iChild, CViewItem *pChild)
{
    SIZE size = { 0 };
    DWORD dwItemDrawFlags = 0;

    if ((DWORD)iChild == m_nCurrentItem)
    {
        dwItemDrawFlags = VIEW_ITEM_FLAG_SELECTED;

        //
        // #9576
        // special handling for long single line menu item
        //
        if (((CViewMenuItem *)pChild)->IsLongSingleLineItem())
        {
            bottom = top + ((CViewMenuItem *)pChild)->GetSelectedItemHeight();
        }
    }

    pChild->Draw(hdc, left, top, right, bottom, dwItemDrawFlags);

    size.cx = right - left;
    size.cy = bottom - top;
    
    return size;
}

//
// <menu> key event handler
//
// S_OK   : handled
// S_FALSE: not handled
// E_XXX  : error
//
HRESULT CMenuView::OnDeviceEvent(CDevice::DeviceEvent DeviceEvent)
{
    HRESULT hr = S_FALSE;
    
    switch(DeviceEvent)
    {
        case CDevice::ButtonSelect:
        {
            if (m_nCurrentItem < m_items.size())
            {
                CNodeApplication *pAppNode = g_pDataManager->GetCurrentApplication();
                CScfMenuElement *pMenu = (CScfMenuElement *)m_pScfContentBase;
                CScfItemElement *pItem = (CScfItemElement *)m_items[m_nCurrentItem]->GetBaseElement();
                
                if (pMenu->m_selectaction == SCF_SELECTACTION_MENUID && pItem->HasMenuIdAttr())
                {
                    g_pDataManager->SetContextMenu(pItem->m_menuId, pItem->m_id);

                    hr = S_OK;
                }
                else
                {
                    if (IsEqualGUID(pAppNode->m_guidApplicationId, GUID_NULL))  // internal application has id = GUID_NULL
                    {
                        //
                        // special handling for internal application
                        //
                        hr = g_pDataManager->HandleInternalCommand(
                                            g_pDataManager->GetCurrentContentId(),
                                            pItem->m_targetId,
                                            (LONG)m_nCurrentItem);
                    }
                    else
                    {
                        g_pDataManager->GetDDI()->SendMenuActionEvent(
                            pAppNode->m_guidApplicationId,              // GadgetId
                            g_pDataManager->GetCurrentContentId(),      // PreviousPage
                            pItem->m_targetId,          // TargetPage
                            SCF_BUTTON_SELECT,
                            pItem->m_id
                            );
                        
                        if (g_pDataManager->GetCurrentContentId() != pItem->m_targetId)
                        {
                            g_pDataManager->PushCurrentContentId();

                            g_pDataManager->SetCurrentContentId(pItem->m_targetId);

                            CContent *pContent = pAppNode->GetContent(pItem->m_targetId);
                            if (pContent)
                            {
                                pContent->OnContentEnter();
                            }
                        }

                        hr = S_OK;
                    }
                }
            }
            break;
        }
        case CDevice::ButtonMenu:
        {
            CONTENT_ID menuId = 0;

            if (m_nCurrentItem < m_items.size())
            {
                CScfItemElement *pItem = (CScfItemElement *)m_items[m_nCurrentItem]->GetBaseElement();

                menuId = pItem->m_menuId;
            }

            //
            // menuId could be 0
            // default context menu will be used when menuId attribute is not specified.
            //
            g_pDataManager->SetContextMenu(menuId);
            hr = S_OK;
            break;
        }
        case CDevice::ButtonUp:
        {
            if (m_nCurrentItem > 0)
            {
                if (m_items[m_nCurrentItem - 1]->GetBaseElementType() != SCF_DIV_ELEMENT)
                {
                    m_nCurrentItem--;
                    hr = S_OK;
                }
                else
                {
                    if (m_nCurrentItem > 1)
                    {
                        m_nCurrentItem -= 2;
                        hr = S_OK;
                    }
                }
                
                if (hr == S_OK)
                {
                    _AdjustViewTopOffset();
                }
            }
            else if (m_nCurrentItem == 0 && m_items.size() > 0)
            {
                m_nCurrentItem = m_items.size() - 1; // wrap from top item in menu list to bottom item (assumes bottom item is not a div element)
                hr = S_OK;
                _AdjustViewTopOffset();
            }

            break;
        }
        case CDevice::ButtonDown:
        {
            if (m_items.size() > 0 && m_nCurrentItem < (m_items.size() - 1))
            {
                if (m_items[m_nCurrentItem + 1]->GetBaseElementType() != SCF_DIV_ELEMENT)
                {
                    m_nCurrentItem++;
                    hr = S_OK;
                }
                else
                {
                    if (m_nCurrentItem < (m_items.size() - 2))
                    {
                        m_nCurrentItem += 2;
                        hr = S_OK;
                    }
                }

                if (hr == S_OK)
                {
                    _AdjustViewTopOffset();
                }
                
            }
            else if (m_nCurrentItem == m_items.size() - 1)
            {
                m_nCurrentItem = 0; // wrap from bottom item in menu list to top item (assumes top item is not a div element)
                hr = S_OK;
                _AdjustViewTopOffset();
            }
            break;
        }
    }
    
    return hr;
}

//
// Adjusting ViewTopOffset
//
VOID CMenuView::_AdjustViewTopOffset()
{
    if (m_nContentHeight <= m_nClientHeight)
    {
        m_nViewTopOffset = 0;
    }
    else
    {
        CViewMenuItem *pCurrItem = (CViewMenuItem *)m_items[m_nCurrentItem];
        int nCurrItemHeight;
        int nContentHeight;
        
//        if (GetViewType() == SCF_MENU_VIEW && pCurrItem->IsLongSingleLineItem())
        if (pCurrItem->IsLongSingleLineItem())
        {
            nCurrItemHeight = pCurrItem->GetSelectedItemHeight();
            nContentHeight = m_nContentHeight - pCurrItem->GetHeight() + pCurrItem->GetSelectedItemHeight();
        }
        else
        {
            nCurrItemHeight = pCurrItem->GetHeight();
            nContentHeight = m_nContentHeight;
        }

        if (nCurrItemHeight >= m_nClientHeight)
        {
            // abnormal case. current item height is larger than client height.
            m_nViewTopOffset = pCurrItem->GetYOffset();
        }
        else
        {
            if (pCurrItem->GetYOffset() < m_nViewTopOffset)
            {
                m_nViewTopOffset = pCurrItem->GetYOffset();
            }
            else
            {
                if ((pCurrItem->GetYOffset() + nCurrItemHeight) <= (m_nViewTopOffset + m_nClientHeight))
                {
                    // OK. The entire current Item is visible.
                }
                else
                {
                    // in case, bottom part of the current Item is not visible.
                    m_nViewTopOffset = pCurrItem->GetYOffset() + nCurrItemHeight - m_nClientHeight;
                }
            }
            
            if ((nContentHeight - m_nViewTopOffset) < m_nClientHeight)
            {
                m_nViewTopOffset = nContentHeight - m_nClientHeight;
            }
        }
    }
}

VOID CMenuView::DrawScrollbar(HDC hdc,  int left, int top, int right, int bottom)
{
    // let renderer draw scrollbar
    RECT rect = { left, top, right, bottom };
    CViewMenuItem *pCurrItem = (CViewMenuItem *)m_items[m_nCurrentItem];
    LONG nContentHeight;
    
    if (pCurrItem->IsLongSingleLineItem())
    {
        nContentHeight = m_nContentHeight - pCurrItem->GetHeight() + pCurrItem->GetSelectedItemHeight();
    }
    else
    {
        nContentHeight = m_nContentHeight;
    }
    
    g_pDataManager->GetRenderer()->DrawScrollbar(
                                        hdc,
                                        rect,
                                        m_nViewTopOffset,
                                        nContentHeight);
}

/*
    Initializing ContextMenu View
*/
HRESULT CContextMenuView::Initialize(CScfContentBase *pScfContentBase)
{
    HRESULT hr = S_OK;

    // should not add "home" and "back" item to pScfContentBase.
    // because context menu and normal menu could share base content.
    
    hr = __super::Initialize(pScfContentBase);

#if 1
    /*
    Context menus should always contain Back and Home items that are provided by the device.
    These items should be appended to any context menu that is provided by the gadget.
    In cases where the gadget does not provide a context menu, a menu generated by the device
    containing only Back and Home should be provided. 
    */
	//if on the home page, do not add the back and home elements
	//
	if(g_pDataManager->GetCurrentContentId() != 0 )
	{
		if (SUCCEEDED(hr))
		{
			if (m_items.size() != 0)
			{
				if (m_items[m_items.size() - 1]->GetBaseElementType() != SCF_DIV_ELEMENT)
				{
					CViewMenuItem *pDivViewItem = NULL;        // Will be freed by CContentViewBase destructor
					hr = _CreateAndAddViewItem(&m_DivElem, &pDivViewItem);
				}
			}
		}

		// add "Back" <item> element
		//
		if (SUCCEEDED(hr))
		{
			hr = m_BackItemElem.SetText(g_pDataManager->GetRenderer()->GetMenuItemBackString());

			if (SUCCEEDED(hr))
			{
				hr = _CreateAndAddViewItem(&m_BackItemElem, &m_pBackViewItem);
			}
		}

		// add "Home" <item> element
		if (SUCCEEDED(hr))
		{
			hr = m_HomeItemElem.SetText(g_pDataManager->GetRenderer()->GetMenuItemHomeString());

			if (SUCCEEDED(hr))
			{
				hr = _CreateAndAddViewItem(&m_HomeItemElem, &m_pHomeViewItem);
			}
		}
	}

#endif

    // Check for the case that item count is changed (e.g., add content n item -> view created -> add content n-1 item).
    if (m_nCurrentItem >= m_items.size())
    {
        m_nCurrentItem = m_items.size() - 1;

        _AdjustViewTopOffset();
    }

    return hr;
}

// helper function for Initialize()
HRESULT CContextMenuView::_CreateAndAddViewItem(CScfElement* pBaseElem, CViewMenuItem **ppViewItem)
{
    HRESULT hr = S_OK;
    CViewMenuItem *pViewItem;
    
    pViewItem = new(std::nothrow) CViewMenuItem(pBaseElem, this);
    
    if (pViewItem)
    {
        hr = pViewItem->Initialize();
        
        if (SUCCEEDED(hr))
        {
            hr = AddViewItem(pViewItem);
        }
        
        if (SUCCEEDED(hr))
        {
            *ppViewItem = pViewItem;
        }
        else
        {
            delete pViewItem;
        }
    }
    else
    {
        hr = E_OUTOFMEMORY;
    }
    
    return hr;
}

//
// Arguments:
//    nClientWidth  = ScreenWidth
//    nClientHeight = ScreenHeigh
//
HRESULT CContextMenuView::OnSize(HDC hDC, LONG nClientWidth, LONG nClientHeight)
{
    HRESULT hr = S_OK;

    nClientWidth -= (CONTENT_LEFT_MARGIN + CONTENT_RIGHT_MARGIN);   // max context menu width

    hr = __super::OnSize(hDC, nClientWidth, nClientHeight);

    //
    // calculate context menu height
    //
#ifdef _THICK_FRAME_CONTEXT_MENU
    // thick bordered new context menu
    if (m_nContentHeight < (LONG)(CDevice::GetDeviceVerticalResolution() - POPUP_FRAME_HEIGHT * 2))
    {
        m_nClientHeight = m_nContentHeight;
    }
    else
    {
        m_nClientHeight = CDevice::GetDeviceVerticalResolution() - POPUP_FRAME_HEIGHT * 2;
    }
#else
    //  default windows type context menu
    if (m_nContentHeight < (LONG)(CDevice::GetDeviceVerticalResolution() - TITLEBAR_HEIGHT))
    {
        m_nClientHeight = m_nContentHeight;
    }
    else
    {
        m_nClientHeight = CDevice::GetDeviceVerticalResolution() - TITLEBAR_HEIGHT - ((CONTEXT_MENU_ARROW_HEIGHT + 2) * 2);
    }
#endif

    //
    // shorten context menu width if possible.
    //
    LONG nMaxItemExtent = CDevice::GetDeviceHorizontalResolution() * 1 / 3;  // starting with minimum width
    
    for(size_t i = 0; i < m_items.size(); i++)
    {
        CViewMenuItem* pViewItem = (CViewMenuItem*)m_items[i];
        
        if (pViewItem->GetBaseElementType() == SCF_ITEM_ELEMENT)
        {
            LONG nItemExtent = pViewItem->GetItemExtent(hDC);
            
            if (nItemExtent > nMaxItemExtent)
            {
                nMaxItemExtent = nItemExtent;
                
                if (nMaxItemExtent > nClientWidth)
                {
                    break;
                }
            }
        }
    }
    
    m_nClientWidth = min(nMaxItemExtent, nClientWidth);
    
    return hr;
}

//
// left, top, right, bottom = screen rect
//
HRESULT CContextMenuView::OnPaint(HDC hdc, int left, int top, int right, int bottom)
{
#ifdef _THICK_FRAME_CONTEXT_MENU
    //
    // draw right aligned thick frame context menu with scrollbar
    //
    left = right - m_nClientWidth - POPUP_FRAME_WIDTH;
    if (IsFlagSet(CONTENT_VIEW_FLAG_SCROLLBAR))
    {
        left -= SCROLLBAR_WIDTH;
    }
    right = right - POPUP_FRAME_WIDTH;
    top = ((bottom - top) - m_nClientHeight) / 2;
    bottom = top + m_nClientHeight;
    
    RECT rect = { left, top, right, bottom };
    
    InflateRect(&rect, POPUP_FRAME_WIDTH, POPUP_FRAME_HEIGHT);
    
    g_pDataManager->GetRenderer()->DrawPopupFrame(hdc, rect);
#else
    //
    // draw windows style context menu with up/down arrow.
    //
    left = ((right - left) - m_nClientWidth) / 2;
    top = ((bottom - top) - m_nClientHeight) / 2;
    right = left + m_nClientWidth;
    bottom = top + m_nClientHeight;
    
    BOOL bUpArrow = FALSE;
    BOOL bDownArrow = FALSE;
    RECT rect = { left, top, right, bottom };

    if (m_nViewTopOffset)
    {
        rect.top -= CONTEXT_MENU_ARROW_HEIGHT + 2;
        bUpArrow = TRUE;
    }
    
    if ((m_nViewTopOffset + m_nClientHeight) < m_nContentHeight)
    {
        rect.bottom += CONTEXT_MENU_ARROW_HEIGHT + 2;
        bDownArrow = TRUE;
    }
    
    InflateRect(&rect, 3, 3);
    Draw3dRect(hdc, rect);
    InflateRect(&rect, -3, -3);

    if (bUpArrow || bDownArrow)
    {
        POINT vertex[3];

        HBRUSH hbrOld = (HBRUSH)SelectObject(hdc, GetStockObject(BLACK_BRUSH));
        
        vertex[0].x = (rect.left + rect.right) / 2;
        vertex[1].x = vertex[0].x - CONTEXT_MENU_ARROW_HEIGHT + 1;
        vertex[2].x = vertex[0].x + CONTEXT_MENU_ARROW_HEIGHT - 1;

        if (bUpArrow)
        {
            vertex[0].y = rect.top + 1;
            vertex[1].y = top - 2;
            vertex[2].y = top - 2;

            Polygon(hdc, vertex, 3);
        }
        
        if (bDownArrow)
        {
            vertex[0].y = rect.bottom - 2;
            vertex[1].y = bottom + 1;
            vertex[2].y = bottom + 1;

            Polygon(hdc, vertex, 3);
        }

        SelectObject(hdc, hbrOld);
    }
#endif
    
    //
    // draw context menu items
    //
    return __super::OnPaint(hdc, left, top, right, bottom);
}

HRESULT CContextMenuView::OnDeviceEvent(CDevice::DeviceEvent DeviceEvent)
{
    HRESULT hr = S_FALSE;
    
    switch(DeviceEvent)
    {
        case CDevice::ButtonSelect:
        {
            if (m_nCurrentItem < m_items.size())
            {
                if (m_items[m_nCurrentItem] == m_pBackViewItem)
                {
                    //
                    // special handling for Back item
                    //
                    g_pDataManager->ClearContextMenu();
                    
                    g_pDataManager->HandleBackButton();
                    
                    hr = S_OK;
                }
                else if (m_items[m_nCurrentItem] == m_pHomeViewItem)
                {
                    //
                    // special handling for Home item
                    //
                    g_pDataManager->ClearContextMenu();
                    
                    g_pDataManager->GotoHome();
                    
                    hr = S_OK;
                }
                else
                {
                    CScfItemElement *pItem = (CScfItemElement *)m_items[m_nCurrentItem]->GetBaseElement();
                    CNodeApplication *pAppNode = g_pDataManager->GetCurrentApplication();

                    if (!pAppNode)
                    {
                        //
                        // special handling for home screen context menu
                        //
                        hr = g_pDataManager->HandleInternalCommand(
                                            0,  // contentId
                                            pItem->m_targetId,
                                            (LONG)m_nCurrentItem);

                        g_pDataManager->ClearContextMenu();
                    }
                    else
                    {
                        CContent *pContent = pAppNode->GetContent(g_pDataManager->GetCurrentContentId());

                        g_pDataManager->GetDDI()->SendContextMenuEvent(
                            pAppNode->m_guidApplicationId,                      // GadgetId
                            g_pDataManager->GetCurrentContentId(),              // PreviousPage
                            pItem->m_targetId,                                  // TargetPage
                            g_pDataManager->GetContextMenuPreviousItemId(),     // PreviousItemId
                            g_pDataManager->GetContextMenuId(),                 // MenuPage
                            pItem->m_id);                                       // MenuItemId

                        g_pDataManager->ClearContextMenu();

                        if (g_pDataManager->GetCurrentContentId() != pItem->m_targetId)
                        {
                            if (pContent->IsMenuContent())
                            {
                                g_pDataManager->PushCurrentContentId();
                            }

                            g_pDataManager->SetCurrentContentId(pItem->m_targetId);

                            pContent = pAppNode->GetContent(pItem->m_targetId);

                            if (pContent)
                            {
                                pContent->OnContentEnter();
                            }
                        }
                        hr = S_OK;
                    }
                }
            }
            break;
        }
        case CDevice::ButtonBack:
        case CDevice::ButtonMenu:
        {
            g_pDataManager->ClearContextMenu();
            hr = S_OK;
            break;
        }
        case CDevice::ButtonUp:
        case CDevice::ButtonDown:
        {
            hr = __super::OnDeviceEvent(DeviceEvent);
            break;
        }
    }
    
    return hr;
}
